using System;
using System.Collections.Generic;

using TrueCode.Core.Aspects;


namespace TrueCode.DependencyManager
{
  /// <summary>
  /// Represents a collection of <see cref="IBuildAction">build actions</see>
  /// </summary>
  public class BuildActionCollection
  {
    #region Fields
    private readonly Dictionary< ActionKey, IBuildAction > _actions = new Dictionary< ActionKey, IBuildAction >();
    #endregion

    #region Entry points
    public BuildActionCollection AddDefault( [NotNull] object buildStep, [NotNull] IBuildAction action )
    {
      #region Validation
      if( buildStep == null ) throw new ArgumentNullException( "buildStep" );
      if( action == null ) throw new ArgumentNullException( "action" );
      #endregion

      _actions.Add( new ActionKey( buildStep, null ), action );
      return this;
    }

    public BuildActionCollection Add( [NotNull] object buildStep, [NotNull] SessionAim aim,
                                      [NotNull] IBuildAction action )
    {
      #region Validation
      if( buildStep == null ) throw new ArgumentNullException( "buildStep" );
      if( aim == null ) throw new ArgumentNullException( "aim" );
      if( action == null ) throw new ArgumentNullException( "action" );
      #endregion

      _actions.Add( new ActionKey( buildStep, aim ), action );
      return this;
    }

    public IBuildAction Get( [NotNull] object buildStep, [NotNull] SessionAim aim )
    {
      #region Validation
      if( buildStep == null ) throw new ArgumentNullException( "buildStep" );
      if( aim == null ) throw new ArgumentNullException( "aim" );
      #endregion

      IBuildAction action;
      if( !_actions.TryGetValue( new ActionKey( buildStep, aim ), out action ) )
        _actions.TryGetValue( new ActionKey( buildStep, null ), out action );

      return action;
    }

    public IBuildAction GetNoDefault( object buildStep, SessionAim aim )
    {
      IBuildAction action;
      _actions.TryGetValue( new ActionKey( buildStep, aim ), out action );
      return action;
    }
    #endregion

    #region ActionKey nested class
    /// <summary>
    /// Instances of this class are used as keys in the <see cref="_actions"/> dictionary.
    /// </summary>
    private class ActionKey : IEquatable< ActionKey >
    {
      #region Fields
      [CanBeNull] private readonly SessionAim _aim;
      [NotNull] private readonly object _buildStep;
      #endregion

      #region .ctors
      /// <summary>
      /// Initializes a new instance of the <see cref="ActionKey"/> class.
      /// </summary>
      /// <param name="buildStep">The type of the value stored in the dictionary.</param>
      /// <param name="aim">Extern key to associate the value with it. Can be null.</param>
      public ActionKey( [NotNull] object buildStep, [CanBeNull] SessionAim aim )
      {
        if( buildStep == null ) throw new ArgumentNullException( "buildStep" );

        _aim = aim;
        _buildStep = buildStep;
      }
      #endregion

      #region Implementation of IEquatable< ActionKey >
      public bool Equals( ActionKey other )
      {
        return Equals( _aim, other._aim ) && _buildStep.Equals( other._buildStep );
      }
      #endregion

      #region Implementation of object equality
      public override bool Equals( object obj )
      {
        return obj is ActionKey ? Equals( ( ActionKey )obj ) : false;
      }

      public override int GetHashCode()
      {
        return ( Equals( _aim, null ) ? 0 : _aim.GetHashCode() ) ^ _buildStep.GetHashCode();
      }
      #endregion
    }
    #endregion
  }
}